﻿#include "libcomm.h"
#ifdef COMM_MSVC
#if (_WIN32_WINNT >= 0x0600)
// Minimum supported client	Windows Vista [desktop apps | UWP apps]
// Minimum supported server	Windows Server 2003 [desktop apps | UWP apps]
#include <fibersapi.h>
#endif
#endif

typedef struct 
{
	TlsVar* inst;
	void* val;
}TlsParam;


static void
#ifdef COMM_MSVC
__stdcall
#endif
tls_thread_local_free(void* param_)
{
	TlsParam* param = (TlsParam*)param_;
	//LOGINFO("TLS(%d:0x%p) destory...", param->inst->Key(), param->val);
	param->inst->Factory()->Destroy(param->val);
	delete param;
}


TlsVar::TlsVar(TlsVarFactory* factory) : m_factory(factory)
{
	MYLIB_ASSERT_NOTNULL(m_factory);
#ifdef COMM_MSVC
#if (_WIN32_WINNT >= 0x0600)
	if ((m_key = FlsAlloc(tls_thread_local_free)) == FLS_OUT_OF_INDEXES)
	{
		LOGFUNCERROR(FlsAlloc);
	}
#else
	if ((m_key = TlsAlloc()) == TLS_OUT_OF_INDEXES)
	{
		LOGFUNCERROR(TlsAlloc);
	}
#endif
#else
	if (0 != pthread_key_create(&m_key, tls_thread_local_free))
	{
		LOGFUNCERROR(pthread_key_create);
	}
#endif
}

TlsVar::~TlsVar()
{
#ifdef COMM_MSVC
#if (_WIN32_WINNT >= 0x0600)
	if (!FlsFree(m_key))
	{
		LOGFUNCERROR(FlsFree);
	}
#else
	if (!TlsFree(m_key))
	{
		LOGFUNCERROR(TlsFree);
	}
#endif
#else
	if (0 != pthread_key_delete(m_key))
	{
		LOGFUNCERROR(pthread_key_delete);
	}
#endif

	if (NULL != m_factory)
	{
		m_factory->Release();
		m_factory = NULL;
	}
}

TlsVarFactory* TlsVar::Factory()
{
	return m_factory;
}

TlsVar::TlsKey TlsVar::Key()
{
	return m_key;
}

void* TlsVar::Value()
{
	TlsParam* param = NULL;
#ifdef COMM_MSVC
#if (_WIN32_WINNT >= 0x0600)
	param = (TlsParam*)FlsGetValue(m_key);
#else
	param = (TlsParam*)TlsGetValue(m_key);
#endif
	if (NULL == param && NO_ERROR == GetLastError())
	{
		param = new TlsParam();
		MYLIB_ASSERT_NOTNULL(param);
		param->inst = this;
		param->val = m_factory->Create();
		MYLIB_ASSERT_NOTNULL(param->val);
		
#if (_WIN32_WINNT >= 0x0600)
		if (!FlsSetValue(m_key, param))
		{
			LOGFUNCERROR(FlsSetValue);
#else
		if (!TlsSetValue(m_key, param))
		{
			LOGFUNCERROR(TlsSetValue);
#endif
	
			m_factory->Destroy(param->val);
			delete param;
			return NULL;
		}
	}
#else
	param = (TlsParam*)pthread_getspecific(m_key);
	if (NULL == param)
	{
		param = new TlsParam();
		MYLIB_ASSERT_NOTNULL(param);
		param->inst = this;
		param->val = m_factory->Create();
		MYLIB_ASSERT_NOTNULL(param->val);

		if (0 != pthread_setspecific(m_key, param))
		{
			LOGFUNCERROR(pthread_setspecific);
			m_factory->Destroy(param->val);
			delete param;
			return NULL;
		}
	}
#endif
	return param->val;
}
